只有看這些名詞解釋是不是霧煞煞,現在就由我來導覽一下他們在程式碼上的實際應用吧。
大家可配合官網上的code 來解讀,或是透過 codeSandBox 一同服用。
官網網址:https://redux-toolkit.js.org/tutorials/typescript
1.在 store.ts
做一些基礎設定,透過 configureStore() 建立 Redux Store。configureStore 裡面可以設定很多擴充套件,比如 Redux-Saga 他是一個 middlerware 就可以直接引入 RTK 裡一同使用。
// app/store.ts
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
// ? configureStore 是優化redux createStore的寫法,可以接收 reducer 後建立 store,代入已經在slice產生好的reducer
// 透過 configureStore() 建立 Redux Store
export const store = configureStore({
reducer: {
counter: counterReducer
}
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
// 在這邊先行定義 redux-thunk type
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
2.引入 store.ts
,被包裹在 Provider 底下的都可以去操作 store 的 state
// index.tsx
import { Provider } from "react-redux";
import { store } from "./app/store";
// ... 省略
<Provider store={store}>
<App />
</Provider>
3.Counter.tsx
是範例中主要讀取資料以及控制 state 的元件,useAppSelector 可拿取資料, useAppDispatch 可以操作資料,他們兩者都在hook.ts
中被定義。每一個按鈕都會去 dispatch 對應 action。
// Counter.tsx
import { useAppSelector, useAppDispatch } from "../../app/hook";
// ... 這裡省略非常多
<button
className={styles.button}
onClick={() => dispatch(incrementByAmount(incrementValue))}
>
Add Amount
</button>
4.counterSlice.ts
是設定 slice redurcer 的檔案
// counterSlice.ts
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
// ... 這裡省略一些設定
// createAsyncThunk 在此先行設定
export const incrementAsync = createAsyncThunk(...);
// 建立好的 slice 可以產生 reducer 和 action creators 供後續使用
export const counterSlice = createSlice({
name: "counter",
initialState,
// counterSlice 中,其 state 的型別會被 initialState 的型別所決定
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
// action.payload 是接受元件中傳入的 data 來做後續操作
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
}
},
// extraReducers 區塊是來操作上方宣告的 createAsyncThunk function
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = "loading";
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = "idle";
state.value += action.payload;
})
.addCase(incrementAsync.rejected, (state) => {
state.status = "failed";
});
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 在此設定元件中可取用的 state
export const countState = (state: RootState) => state.counter;
// 這邊是介紹 redux-thunk 寫法
export const incrementIfOdd = (amount: number): AppThunk => (
dispatch,
getState
) => {
const currentValue = countState(getState());
if (currentValue.value % 2 === 1) {
dispatch(incrementByAmount(amount));
}
};
export default counterSlice.reducer;
createAsyncThunk
有三種狀態(pending/fulfilled/rejected)可用來表示非同步請求的生命週期,這可以讓我們的程式碼有一些更彈性的設定,比如我在 codeSandBox 增加的 loading 顯示,或是我們的操作執行過程中failed 的話也能有一些更彈性的設定。
還有最後面有稍微帶到的 redux-thunk ,他其實是 function 內包裹 function 寫法,不過我這邊先單純加個判斷式意思意思一下。
結語:
技術更新日新月異,寫法真的也是越來越平易近人了,接下來還有 redux-sage的出現,基本取代了redux-thunk的功能,前幾天跟同事聊到已經用習慣 redux-saga 連 thunk 是啥都不知道了,雖然我在看 RTK 官方文件前也不知道